package demo4

import (
	"errors"
	"fmt"
	"os"
)

/* 日志对外接口 */

// 声明日志写入接口
type logWritter interface {
	write(data interface{}) error
}

// 日志器
type logger46 struct {
	// 这个日志器用到的日志写入器
	writerList []logWritter
}

// 注册一个日志写入器
func (l *logger46) registerWriter(writer logWritter) {
	l.writerList = append(l.writerList, writer)
}

// 将一个 data 类型的数据写入日志
func (l *logger46) log(data interface{}) {
	// 遍历所有注册的写入器
	for _, writer := range l.writerList {
		// 将日志写入到每一个写入器中
		writer.write(data)
	}
}

// 创建日志器实例
func newLogger() *logger46 {
	return &logger46{}
}

/* 文件写入器 */
// 声明文件写入器
type fileWriter struct {
	file *os.File
}

// 设置 fileWriter 写入的文件名
func (f *fileWriter) setFile(filename string) (err error) {
	// 如果文件已经打开，关闭前一个文件
	if f.file != nil {
		f.file.Close()
	}

	// 创建一个文件并保存文件句柄
	f.file, err = os.Create(filename)

	// 如果创建过程出错，则返回错误
	return err
}

// 实现 fileWriter 的 write 方法
func (f *fileWriter) write(data interface{}) error {
	// 日志文件可能没有创建成功
	if f.file == nil {
		// 日志文件没有
		return errors.New("file not created")
	}

	// 将数据序列化为字符串
	str := fmt.Sprintf("%v\n", data)

	// 将数据以字节数组的形式写入文件中
	_, err := f.file.Write([]byte(str))

	return err
}

// 创建 fileWriter 实例
func newFileWriter() *fileWriter {
	return &fileWriter{}
}

/*
	命令行写入器
	  1. 在 UNIX 的思想中，一切皆文件。文件包括内存、磁盘、网络和命令行等。这种抽象方法方便我们访问这些看不见摸不着的虚拟资源。
	  2. 命令行在 Go语言中也是一种文件，
	 		os.Stdout 对应标准输出，一般表示屏幕，也就是命令行，也可以被重定向为打印机或者磁盘文件；
			 os.Stderr 对应标准错误输出，一般将错误输出到日志中
		3. os.Stdout 会与 os.Stderr 合并输出；os.Stdin 对应标准输入，一般表示键盘。
			os.Stdout、os.Stderr、os.Stdin 都是 *os.File 类型，和文件一样实现了 io.Writer 接口的 Write() 方法。
*/
// 命令行写入器
type consoleWriter struct{}

// 实现 logWriter 的 write 方法
func (c *consoleWriter) write(data interface{}) error {
	// 将数据序列化为字符串
	str := fmt.Sprintf("%v\n", data)

	// 将数据以字节数组的形式写入命令行中
	_, err := os.Stdout.Write([]byte(str))

	return err
}

// 创建命令行写入器实例
func newConsoleWriter() *consoleWriter {
	return &consoleWriter{}
}

/*
	使用日志
	1. 在程序中使用日志器一般会先通过代码创建日志器（Logger），为日志器添加输出设备（fileWriter、consoleWriter等）。
			这些设备中有一部分需要一些参数设定，如文件日志写入器需要提供文件名（fileWriter 的 SetFile() 方法）。
*/

// 创建日志器
func createLogger() *logger46 {
	// 创建日志器
	l := newLogger()

	// 创建命令行写入器
	cw := newConsoleWriter()

	// 注册 consoleWriter 到 logger 中
	l.registerWriter(cw)

	// 创建 fileWriter
	fw := newFileWriter()

	// 设置文件名称
	if err := fw.setFile("log.log"); err != nil {
		fmt.Println(err)
	}

	// 注册 fileWriter 到 logger 中
	l.registerWriter(fw)

	return l
}

/*
	Go语言的 log 包实现了一个小型的日志系统。这个日志系统可以在创建日志器时选择输出设备、日志前缀及 flag，函数定义如下：

		func New(out io.Writer, prefix string, flag int) *Logger {
			return &Logger{out: out, prefix: prefix, flag: flag}
		}
*/

// TestDemo46 示例 go 语言的日志系统
func TestDemo46() {
	// 准备 logger
	l := createLogger()

	// 下一个日志
	l.log("Hello, go log")
}
